package cn.hh.user.service.impl;

import cn.hh.basic.constant.RegisterConstant;
import cn.hh.basic.exception.BusinessException;
import cn.hh.basic.jwt.JwtUtils;
import cn.hh.basic.jwt.LoginData;
import cn.hh.basic.jwt.RsaUtils;
import cn.hh.basic.util.AjaxResult;
import cn.hh.basic.util.HttpUtil;
import cn.hh.basic.util.Md5Utils;
import cn.hh.basic.util.StrUtils;
import cn.hh.system.domain.Menu;
import cn.hh.user.constant.WxConstant;
import cn.hh.user.domain.Logininfo;
import cn.hh.user.domain.User;
import cn.hh.user.domain.Wxuser;
import cn.hh.user.dto.BinderDto;
import cn.hh.user.dto.LoginDto;
import cn.hh.user.dto.RegisterDto;
import cn.hh.user.mapper.LogininfoMapper;
import cn.hh.user.mapper.UserMapper;
import cn.hh.user.mapper.WxuserMapper;
import cn.hh.user.service.LogininfoService;
import cn.hh.basic.service.impl.BaseServiceImpl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import ognl.Token;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 业务实现类：
 */
@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements LogininfoService {

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;

    /**
     * 账号登录
     *
     * @param loginDto
     * @return
     */
    @Override
    public Map<String, Object> accountLogin(LoginDto loginDto) {
        // 先获取到要做校验的参数
        String account = loginDto.getAccount(); // 登录账户
        String password = loginDto.getPassword(); // 密码
        Integer loginType = loginDto.getLoginType(); // 登录用户的类型

        // 1.校验 - 非空校验
        if (StringUtils.isEmpty(account) || StringUtils.isEmpty(password) || StringUtils.isEmpty(loginType)) {
            throw new BusinessException("数据不能为空!!!");
        }

        // 2.校验账户
        Logininfo logininfo = logininfoMapper.loadByAccount(loginDto);
        if (logininfo == null) { // 账号为空
            throw new BusinessException("账号或者密码错误!");
        }

        // 3.校验密码
        // 账号不为空就进行密码加严加密
        String md5InputPwd = Md5Utils.encrypByMd5(password + logininfo.getSalt());// 拿到当前账号的密码和盐值,调用Md5工具类进行加密加严
        if (!md5InputPwd.equals(logininfo.getPassword())) // 判断当前加严加密的密码和数据库中存储的用户密码进行比较
            throw new BusinessException("账号或者密码错误!");

        // 4.状态校验 - 启用, 禁用
        if (!logininfo.getDisable()) {
            throw new BusinessException("你的账号已被冻结,请联系管理员!");
        }


        // 4.生成Key[token] 将key和登录信息对象保存到redis
//        String token = UUID.randomUUID().toString();
//        // 去掉密码和盐值 - 安全
//        logininfo.setPassword(null);
//        logininfo.setSalt(null);
//        redisTemplate.opsForValue().set(token, logininfo, 30, TimeUnit.MINUTES);
//
//        // 将token和logininfo传给前端
//        Map<String, Object> map = new HashMap<>();
//        map.put("token", token);
//        map.put("logininfo", logininfo);


        Map<String, Object> map = loginSuccessJwtHandler(logininfo);

        return map;
    }

    /**
     *
     * @param logininfo
     * @return
     */
    private Map<String, Object> loginSuccessJwtHandler(Logininfo logininfo) {
        // 创建LoginData对象
        LoginData loginData = new LoginData();
        //
        logininfo.setSalt(null);
        logininfo.setPassword(null);
        // loginData对象获取logininfo登录信息对象
        loginData.setLogininfo(logininfo);
        // 创建一个Map集合,添加token,logininfo,permissions,menus
        HashMap<String, Object> map = new HashMap<>();
        try {
            // 1.获取logininfo
            if (logininfo.getType() == 0) { // 登录类型 = 0 就是管理员
                // 2.获取当前登录人的所有权限 - sn
                List<String> permissions = logininfoMapper.loadPermissionsByLogininId(logininfo.getId());
                // 3.获取当前登录人的所有菜单 - [难度]
                List<Menu> menus = logininfoMapper.loadMenuByLogininId(logininfo.getId());
                // loginData装入permissions登录人的所有权限
                loginData.setPermissions(permissions);
                // loginData装入当前登录人的所有菜单
                loginData.setMenus(menus);

                // 5.1.将permissions和menus装入到map中去
                map.put("permissions",permissions);
                map.put("menus",menus);
            }
            // 4.通过私钥对登录信息进行加密 - jwtToken串  拿到私钥了
            PrivateKey privateKey = RsaUtils.getPrivateKey(RsaUtils.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            // 用私钥进行加密了  1.存入loginData对象, 2.privateKey私钥,  3.设置过期时间
            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 3000);

            // 5.装到map进行返回
            map.put("token",jwtToken);
            map.put("logininfo",logininfo);

            return map;

        }catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }


    /**
     * 微信登录
     *
     * @param code
     * @return
     */
    @Override
    public AjaxResult wechatLogin(String code) {
        // 1.根据code发送请求获取:token和openid
        // 通过常量类获取到请求地址
        String url = WxConstant.GET_ACK_URL
                .replace("APPID", WxConstant.APPID) // 将地址栏上的APPID替换为常量中的APPID
                .replace("SECRET", WxConstant.SECRET) // 将地址栏上的SECRET替换为常量中的SECRET
                .replace("CODE", code); // 将地址栏上的CODE替换为获取到的code
        // 通过HttpUtil工具类调用httpGet方法传入url返回json格式的字符串
        String jsonStr = HttpUtil.httpGet(url);
        // 2.将响应的json字符串 转成json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        // 拿到token
        String accessToken = jsonObject.getString("access_token");
        // 拿到openid
        String openid = jsonObject.getString("openid");
        // 3.通过openid去t_wxuser中查询
        Wxuser wxuser = wxuserMapper.loadByOpenid(openid);

        // 4.如果查询到 - 绑定过 - 直接免密登录[获取logininfo对象 登录]
        if (wxuser != null && wxuser.getUserId() != null) { // 绑定了
            Logininfo logininfo = logininfoMapper.loadlogininfoByUserId(wxuser.getUserId());

            Map<String, Object> map = loginSuccessJwtHandler(logininfo);

            return AjaxResult.me().setResultObj(map);

        } else { // 5.如果没有 - 需要跳转页面绑定 - 响应给前端进行跳转
            Map<String, Object> map = new HashMap<>();
            // 获取到accessToken和openid
            map.put("accessToken", accessToken);
            map.put("openid", openid);
            return AjaxResult.me().setSuccess(false).setResultObj(map);
        }


    }

    /**
     * 微信绑定
     *
     * @param binderDto
     * @return
     */
    @Override
    public AjaxResult wechatBinder(BinderDto binderDto) {
        // 先将数据获取出来
        String phone = binderDto.getPhone(); // 手机号
        String VerifyCode = binderDto.getVerifyCode();  // 验证码
        String accessToken = binderDto.getAccessToken();
        String openId = binderDto.getOpenId();

        // 1.校验 - 非空校验
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(VerifyCode)) {
            throw new BusinessException("数据不能为空");
        }

        // 2.校验 - 验证码是否过期
        Object obj = redisTemplate.opsForValue().get(phone + RegisterConstant.PHONEREGISTER);
        if (obj == null) {
            throw new BusinessException("验证码过期");
        }

        // 3.校验 - 验证码是否正确
        if (!obj.toString().split(":")[0].equalsIgnoreCase(VerifyCode.trim())) {
            throw new BusinessException("验证码错误");
        }

        // 4.绑定用户 - t_wxUser[user_id] 绑定 t_user
        // 4.1.获取WxUser - 发送第三个请求 - json字符串
        String url = WxConstant.GET_USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        String jsonStr = HttpUtil.httpGet(url);

        // 转成wxuser对象
        Wxuser wxuser = jsonStr2Wxuser(jsonStr); // user_id

        // 5.通过phone查询User
        User user = userMapper.loadByPhone(phone); // 如果有就直接用这里的user

        // 5.1如果没有 - 根据phone创建User对象 - 创建Logininfo对象 - 先保存Logininfo对象 - 在保存User对象
        if (user == null) {
            // 根据phone创建User对象
            user = phone2User(phone);
            //创建Logininfo对象
            Logininfo logininfoTmp = user2Logininfo(user); //
            // 保存 logininfo
            logininfoMapper.save(logininfoTmp);
            // 设置user的LogininfoId,从logininfoTmp(临时对象)中获取
            user.setLogininfoId(logininfoTmp.getId());
            // 添加user
            userMapper.save(user);
        }
        // 6.将User对象的id设置到WxUser对象中
        wxuser.setUserId(user.getId());

        // 7.将WxUser对象添加到数据库
        wxuserMapper.save(wxuser);

        // 8.做免密登录
        Logininfo logininfo = logininfoMapper.loadById(user.getLogininfoId());

        Map<String, Object> map = loginSuccessJwtHandler(logininfo);

        return new AjaxResult().me().setResultObj(map);
    }

    /**
     * 用户退出
     *
     * @param token
     */
    @Override
    public void logout(String token) {
        // 从redis删除token
        redisTemplate.delete(token);
    }

    private Logininfo user2Logininfo(User user) {
        Logininfo logininfo = new Logininfo(); // 创建一个logininfo对象
        BeanUtils.copyProperties(user, logininfo); // copy相同属性 源:user 目标:logininfo
        logininfo.setType(1);  // logininfo设置登录类型
        return logininfo;
    }

    private User phone2User(String phone) {
        // 创建一个user对象
        User user = new User();
        // user对象的用户名设置为手机号
        user.setUsername(phone);
        // user对象设置手机号
        user.setPhone(phone);
        // 获取一个加加严的对象
        String salt = StrUtils.getComplexRandomString(32);
        // 获取一个随机的纯数字密码
        String randomPwd = StrUtils.getRandomString(6);
        // 通过工具类加密加严
        String md5Pwd = Md5Utils.encrypByMd5(randomPwd + salt);
        // user对象设置加严
        user.setSalt(salt);
        // user对象设置密码
        user.setPassword(md5Pwd);
        return user;

    }

    private Wxuser jsonStr2Wxuser(String jsonStr) {
        // 创建一个wxuser对象
        Wxuser wxuser = new Wxuser();
        // 将json字符串转成json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        // wxuser设置json对象的每个字段的值
        wxuser.setOpenid(jsonObject.getString("openid"));
        wxuser.setNickname(jsonObject.getString("nickname"));
        wxuser.setSex(jsonObject.getInteger("sex"));
        wxuser.setAddress(jsonObject.getString("country") + jsonObject.getString("province") + jsonObject.getString("city"));
        wxuser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxuser.setUnionid(jsonObject.getString("unionid"));
        return wxuser;
    }


}
